/* ****************************************************************************
 * Copyright (c) TCC - 2009 IME - USP. 
 *
 * This source code is subject to terms and conditions of the Microsoft Public License. 
 * A copy of the license can be found in http://msdn.microsoft.com/en-us/library/dd228915.aspx. 
 * By using this source code in any fashion, you are agreeing to be bound by the terms of the 
 * Microsoft Public License.
 *
 * You must not remove this notice, or any other, from this software.
 * ***************************************************************************/

namespace SoundLibrary
{
    using System;

    internal class Decoder//// : DecoderErrors
    {
        private static readonly Params DEFAULT_PARAMS = new Params();
        private Obuffer output;
        private SynthesisFilter filter1;
        private SynthesisFilter filter2;
        private LayerIIIDecoder layer3decoder;
        private int outputFrequency;
        private int outputChannels;
        private Equalizer equalizer;
        private Params params_Renamed;
        private bool initialized;

        #region Constructor

        public Decoder()
            : this(null)
        {
            this.InitBlock();
        }

        public Decoder(Params params0)
        {
            this.InitBlock();
            if (params0 == null)
            {
                params0 = DEFAULT_PARAMS;
            }

            this.params_Renamed = params0;

            Equalizer eq = this.params_Renamed.InitialEqualizerSettings;
            if (eq != null)
            {
                this.equalizer.FromEqualizer = eq;
            }
        }

        #endregion

        #region Properties

        public static Params DefaultParams
        {
            get
            {
                return (Params)DEFAULT_PARAMS.Clone();
            }
        }

        public virtual Equalizer Equalizer
        {
            set
            {
                if (value == null)
                {
                    value = Equalizer.PASS_THRU_EQ;
                }

                this.equalizer.FromEqualizer = value;

                float[] factors = this.equalizer.BandFactors;
                if (this.filter1 != null)
                {
                    this.filter1.EQ = factors;
                }

                if (this.filter2 != null)
                {
                    this.filter2.EQ = factors;
                }
            }
        }

        public virtual Obuffer OutputBuffer
        {
            set { this.output = value; }
        }

        public virtual int OutputFrequency
        {
            get { return this.outputFrequency; }
        }

        public virtual int OutputChannels
        {
            get { return this.outputChannels; }
        }

        public virtual int OutputBlockSize
        {
            get { return Obuffer.OBUFFERSIZE; }
        }

        #endregion

        #region Public Methods

        public virtual Obuffer DecodeFrame(Header header, Bitstream stream)
        {
            if (!this.initialized)
            {
                this.Initialize(header);
            }

            int layer = header.Layer();

            this.output.clear_buffer();

            LayerIIIDecoder decoder = this.RetrieveDecoder(header, stream, layer);
            ////FrameDecoder decoder = retrieveDecoder(header, stream, layer);

            decoder.decodeFrame();

            this.output.write_buffer(1);

            return this.output;
        }

        #endregion

        #region Protected Methods

        protected internal virtual LayerIIIDecoder RetrieveDecoder(Header header, Bitstream stream, int layer)
        {
            LayerIIIDecoder decoder = null;

            switch (layer)
            {
                case 3:
                    if (this.layer3decoder == null)
                    {
                        this.layer3decoder = new LayerIIIDecoder(stream, header, this.filter1, this.filter2, this.output, (int)OutputChannelsEnum.BOTH_CHANNELS);
                    }

                    decoder = this.layer3decoder;
                    break;
            }

            if (decoder == null)
            {
                ////throw newDecoderException(javazoom.jl.decoder.DecoderErrors_Fields.UNSUPPORTED_LAYER, null);
            }

            return decoder;
        }

        #endregion

        #region Private Methods

        private void InitBlock()
        {
            this.equalizer = new Equalizer();
        }

        private void Initialize(Header header)
        {
            float scalefactor = 32700.0f;
            int mode = header.Mode();
            int layer = header.Layer();
            int channels = mode == Header.SINGLE_CHANNEL ? 1 : 2;

            if (this.output == null)
            {
                throw new Exception("fudeu!!!!");
                ////this.output = new SampleBuffer(header.frequency(), channels);
            }

            float[] factors = this.equalizer.BandFactors;
            this.filter1 = new SynthesisFilter(0, scalefactor, factors);

            if (channels == 2)
            {
                this.filter2 = new SynthesisFilter(1, scalefactor, factors);
            }

            this.outputChannels = channels;
            this.outputFrequency = header.Frequency();

            this.initialized = true;
        }

        #endregion

        #region Internal Class
        internal class Params : System.ICloneable
        {
            private OutputChannels outputChannels;
            private Equalizer equalizer;

            #region Constructor

            public Params()
            {
            }

            #endregion

            #region Properties

            public virtual OutputChannels OutputChannels
            {
                get
                {
                    return this.outputChannels;
                }

                set
                {
                    if (value == null)
                    {
                        throw new System.NullReferenceException("out");
                    }

                    this.outputChannels = value;
                }
            }

            public virtual Equalizer InitialEqualizerSettings
            {
                get { return this.equalizer; }
            }

            #endregion

            #region Public Methods

            public object Clone()
            {
                try
                {
                    return base.MemberwiseClone();
                }
                catch (System.Exception ex)
                {
                    throw new System.ApplicationException(this + ": " + ex);
                }
            }

            #endregion

            #region Private Methods

            private void InitBlock()
            {
                this.outputChannels = OutputChannels.BOTH;
                this.equalizer = new Equalizer();
            }

            #endregion
        }
        #endregion
    }
}